#ifndef NANE_INTRUSIVEPTR_H
#   define  NANE_INTRUSIVEPTR_H

#   include "nane/core/assert.h"
#   include "nane/core/types.h"
#   include "nane/core/IRefCounted.h"
#   include "nane/core/NullPtr.h"

namespace nane 
{

    template<class Base = IRefCounted>
    class RefCounted
        : public Base
    {
    public:
        RefCounted()
            : m_refCounter( 0 )
        {
            
        }
            
        ~RefCounted()
        {
            NANE_ASSERT( m_refCounter == 0, "" );
        }
        
    public:
        template<typename T> friend class IntrusivePtr;
        uint32 Ref()
        {
            return ++m_refCounter;
        }
        
        uint32 Unref()
        {
            NANE_ASSERT_RV( m_refCounter > 0, "RefCounter must be > 0", 0 );
            return --m_refCounter;
        }
        
    private:
        uint32 m_refCounter;
    };
    
    template<typename T>        // T inherits IRefCounted
    class IntrusivePtr
    {
    public:
        IntrusivePtr()
            : m_pObj( NULL )
        {
        }
        
        IntrusivePtr( const NullType& _nullPtr ) 
            : m_pObj( NULL ) 
        {
        }

        explicit IntrusivePtr( T* _obj )
            : m_pObj( _obj )
        {
            if( _obj )
            {
                _obj->Ref();
            }
        }
        
        IntrusivePtr( const IntrusivePtr& _other )
            : m_pObj( _other.m_pObj )
        {
            if( _other.m_pObj )
            {
                _other.m_pObj->Ref();
            }
        }

        template<typename U>
        IntrusivePtr( const IntrusivePtr<U>& _other )
            : m_pObj( _other.Get() )
        {
            if( m_pObj )
            {
                m_pObj->Ref();
            }
        }

        ~IntrusivePtr()
        {
            if( m_pObj )
            {
                if( m_pObj->Unref() == 0 )
                {
                    static_cast<IRefCounted*>(m_pObj)->Dispose();
                }
            }
        }
        
    public:
        IntrusivePtr& operator=( const IntrusivePtr& _rhs )
        {
            IntrusivePtr(_rhs).Swap(*this);
            return *this;                
        }

        template<typename U>
        IntrusivePtr& operator=( const IntrusivePtr<U>& _rhs )
        {
            IntrusivePtr(_rhs).Swap(*this);
            return *this;                
        }
        
        IntrusivePtr& operator=( const NullType& _nullPtr )
        {
            IntrusivePtr(NULL).Swap(*this);
            return *this;
        }

        T& operator*() const 
        { 
            NANE_ASSERT( m_pObj != NULL, "Pointer must be valid" );
            return *m_pObj;
        }

        T* operator->() const 
        { 
            NANE_ASSERT( m_pObj != NULL, "Pointer must be valid" );
            return m_pObj; 
        }

        bool operator==( const IntrusivePtr& _rhs ) const
        {
            return Get() == _rhs.Get();
        }

        bool operator!=( const IntrusivePtr& _rhs ) const
        {
            return Get() != _rhs.Get();
        }
        
        bool operator==( const NullType& _nullPtr ) const
        {
            return Get() == NULL;
        }
        
        bool operator!=( const NullType& _nullPtr ) const
        {
            return Get() != NULL;
        }

        T* Get() const
        { 
            return m_pObj; 
        }

        void Reset( T* _obj )
        {
            IntrusivePtr(_obj).Swap(*this);
        }

        void Swap( IntrusivePtr& _other )
        {
            T* obj = _other.m_pObj;
            _other.m_pObj = m_pObj;
            m_pObj = obj;
        }
            
    private:
        T* m_pObj;
    };

}   // namespace nane

#endif	// NANE_INTRUSIVEPTR_H
